/*
 * Copyright (c) 2022-2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "content/renderer/media/ohos/native_renderer_client.h"

#include <utility>

#include "base/functional/bind.h"
#include "base/task/current_thread.h"

namespace content {

NativeRenderClient::NativeRenderClient(
    scoped_refptr<base::SequencedTaskRunner> media_task_runner,
    scoped_refptr<base::SingleThreadTaskRunner> compositor_task_runner,
    media::ScopedNativeTextureWrapper native_texture_wrapper,
    media::VideoRendererSink* sink)
    : MojoRendererWrapper(nullptr),
      media_task_runner_(std::move(media_task_runner)),
      native_texture_wrapper_(std::move(native_texture_wrapper)),
      sink_(sink),
      compositor_task_runner_(std::move(compositor_task_runner)) {}

NativeRenderClient::~NativeRenderClient() {
  // Clearing the STW's callback into |this| must happen first. Otherwise, the
  // underlying StreamTextureProxy can callback into OnFrameAvailable() on the
  // |compositor_task_runner_|, while we are destroying |this|.
  // See https://crbug.com/688466.
  native_texture_wrapper_->ClearCBOnAnyThread();
}

void NativeRenderClient::Initialize(
    media::CreateTextureCB create_texture_cb,
    media::DestroyTextureCB destroy_texture_cb) {
  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());
  create_texture_cb_ = std::move(create_texture_cb);
  destroy_texture_cb_ = std::move(destroy_texture_cb);

  // Unretained is safe here because |native_texture_wrapper_| resets the
  // Closure it has before destroying itself on |compositor_task_runner_|,
  // and |this| is garanteed to live until the Closure has been reset.
  native_texture_wrapper_->Initialize(
      base::BindRepeating(&NativeRenderClient::OnFrameAvailable,
                          base::Unretained(this)),
      compositor_task_runner_, base::DoNothing(),
      base::BindOnce(&NativeRenderClient::OnSurfaceCreated,
                     weak_factory_.GetWeakPtr()),
      base::BindOnce(&NativeRenderClient::OnSurfaceDestroy,
                     weak_factory_.GetWeakPtr()));
}

void NativeRenderClient::OnSurfaceCreated(int native_embed_id) {
  DCHECK(!create_texture_cb_);
  DCHECK(media_task_runner_->RunsTasksInCurrentSequence());

  std::move(create_texture_cb_)
      .Run(base::BindRepeating(&NativeRenderClient::OnSizeChange,
                               weak_factory_.GetWeakPtr()),
           native_embed_id);
}

void NativeRenderClient::OnSurfaceDestroy() {
  if (!destroy_texture_cb_.is_null()) {
    std::move(destroy_texture_cb_).Run();
  }
}

void NativeRenderClient::OnFrameAvailable() {
  DCHECK(compositor_task_runner_->BelongsToCurrentThread());

  // The frame generated by the StreamTextureWrapper is "static", i.e., even as
  // new frames are drawn it does not change. Downstream components expect that
  // each new VideoFrame will have a different unique_id() when it changes, so
  // we need to add a wrapping frame with a new unique_id().
  auto frame = native_texture_wrapper_->GetCurrentFrame();

  auto unique_frame = media::VideoFrame::WrapVideoFrame(
      frame, frame->format(), frame->visible_rect(), frame->natural_size());

  LOG(DEBUG) << "NativeEmbed OnFrameAvailable frame:" << frame
             << ", coded_size:" << frame->coded_size().ToString()
             << ",unique_frame:" << unique_frame;
  sink_->PaintSingleFrame(std::move(unique_frame));
}

void NativeRenderClient::OnSizeChange(const gfx::Rect& rect) {
  native_texture_wrapper_->UpdateTextureSize(rect.size());
}

}  // namespace content
